#include "defs.h"
#if CSVFMTS_ENABLED
-#include <algorithm> // for sort
+#include <algorithm> // for for_each, sort
+#include <array> // for array
#include <cctype> // for toupper
#include <cmath> // for fabs, floor
-#include <cstdio> // for NULL, snprintf, sscanf
-#include <cstdint>
+#include <cstdint> // for uint16_t
+#include <cstdio> // for sscanf, fprintf, snprintf, stderr
#include <cstdlib> // for abs
-#include <cstring> // for memset, strstr, strcat, strchr, strlen, strcmp, strcpy, strncpy
-#include <ctime> // for gmtime, localtime, strftime
+#include <cstring> // for strstr, strlen
+#include <ctime> // for time_t, gmtime, localtime, strftime
+#include <utility> // for pair, make_pair
#include <QByteArray> // for QByteArray
#include <QChar> // for QChar, QChar::Other_Control
+#include <QDateTime> // for QDateTime
#include <QIODevice> // for QIODevice, QIODevice::ReadOnly, QIODevice::WriteOnly
+#include <QList> // for QList, QList<>::const_iterator
#include <QString> // for QString, operator!=
#include <QStringList> // for QStringList
#include <QTextStream> // for QTextStream
#include "src/core/datetime.h" // for DateTime
#include "src/core/logging.h" // for Fatal
#include "src/core/textstream.h" // for TextStream
-#include "strptime.h" // for strptime
#define MYNAME "garmin_txt"
static gpsbabel::TextStream* fin = nullptr;
static gpsbabel::TextStream* fout = nullptr;
-static route_head* current_trk, *current_rte;
+static route_head* current_trk;
+static route_head* current_rte;
static int waypoints;
static int routepoints;
static const Waypoint** wpt_a;
static int current_line;
static char* date_time_format = nullptr;
static int precision = 3;
-static QString current_line_text;
static time_t utc_offs = 0;
static gtxt_flags_t gtxt_flags;
return ret;
}
-#define MAX_HEADER_FIELDS 36
+static std::array<QList<std::pair<QString, int>>, unknown_header> header_mapping_info;
+static QStringList header_column_names;
-static QString header_lines[unknown_header + 1][MAX_HEADER_FIELDS];
-static int header_fields[unknown_header + 1][MAX_HEADER_FIELDS];
-static int header_ct[unknown_header + 1];
+static constexpr double kGarminUnknownAlt = 1.0e25;
+static constexpr char kDefaultDateFormat[] = "dd/mm/yyyy";
+static constexpr char kDefaultTimeFormat[] = "HH:mm:ss";
-#define GARMIN_UNKNOWN_ALT 1.0e25f
-#define DEFAULT_DATE_FORMAT "dd/mm/yyyy"
-#define DEFAULT_TIME_FORMAT "HH:mm:ss"
-
-/* macros */
-
-#define IS_VALID_ALT(a) (((a) != unknown_alt) && ((a) < GARMIN_UNKNOWN_ALT))
+static inline bool is_valid_alt(double alt)
+{
+ return (alt != unknown_alt) && (alt < kGarminUnknownAlt);
+}
static char* opt_datum = nullptr;
static char* opt_dist = nullptr;
{"utc", &opt_utc, "Write timestamps with offset x to UTC time", nullptr, ARGTYPE_INT, "-23", "+23", nullptr},
};
-class PathInfo {
- public:
+class PathInfo
+{
+public:
double length {0};
time_t start {0};
time_t time {0};
static int route_idx;
static PathInfo* cur_info;
-static const char* headers[] = {
+static const QVector<QString> headers = {
"Name\tDescription\tType\tPosition\tAltitude\tDepth\tProximity\tTemperature\t"
"Display Mode\tColor\tSymbol\tFacility\tCity\tState\tCountry\t"
"Date Modified\tLink\tCategories",
"Waypoint Name\tDistance\tLeg Length\tCourse",
"Position\tTime\tAltitude\tDepth\tTemperature\tLeg Length\tLeg Time\tLeg Speed\tLeg Course",
"Name\tLength\tCourse\tWaypoints\tLink",
- "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink",
- nullptr
+ "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink"
};
/* helpers */
// This is old, and weird, code.. date_time_format is a global that's
// explicitly malloced and freed elsewhere. This isn't very C++ at all,
// but this format is on its deathbead for deprecation.
- const char* d = get_option_val(opt_date_format, DEFAULT_DATE_FORMAT);
+ const char* d = get_option_val(opt_date_format, kDefaultDateFormat);
QString d1 = convert_human_date_format(d);
- const char* t = get_option_val(opt_time_format, DEFAULT_TIME_FORMAT);
+ const char* t = get_option_val(opt_time_format, kDefaultTimeFormat);
QString t1 = convert_human_time_format(t);
xasprintf(&date_time_format, "%s %s", CSTR(d1), CSTR(t1));
}
static void
-print_speed(const double* distance, const time_t* time)
+print_speed(const double distance, const time_t time)
{
- double dist = *distance;
+ double dist = distance;
const char* unit;
if (!gtxt_flags.metric) {
}
int idist = qRound(dist);
- if ((*time != 0) && (idist > 0)) {
- double speed = MPS_TO_KPH(dist / (double)*time);
+ if ((time != 0) && (idist > 0)) {
+ double speed = MPS_TO_KPH(dist / (double)time);
int ispeed = qRound(speed);
if (speed < 0.01) {
}
const char* dspl_mode = gt_display_mode_names[i];
- unsigned char wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0);
+ int wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0);
if (wpt_class <= gt_waypt_class_map_line) {
wpt_type = gt_waypt_class_names[wpt_class];
} else {
print_position(wpt);
- if IS_VALID_ALT(wpt->altitude) {
+ if (is_valid_alt(wpt->altitude)) {
print_distance(wpt->altitude, true, false, 0);
}
*fout << "\t";
if (!gtxt_flags.route_header_written) {
gtxt_flags.route_header_written = 1;
- *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n", headers[route_header]);
+ *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n").arg(headers[route_header]);
}
print_string("\r\nRoute\t%s\t", rte->rte_name);
print_distance(cur_info->length, false, true, 0);
} else {
print_string("%s\r\n", "");
}
- *fout << QString::asprintf("\r\nHeader\t%s\r\n\r\n", headers[rtept_header]);
+ *fout << QStringLiteral("\r\nHeader\t%1\r\n\r\n").arg(headers[rtept_header]);
}
static void
if (!gtxt_flags.track_header_written) {
gtxt_flags.track_header_written = 1;
- *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n", headers[track_header]);
+ *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n").arg(headers[track_header]);
}
print_string("\r\nTrack\t%s\t", track->rte_name);
print_date_and_time(cur_info->start, false);
print_date_and_time(cur_info->time, true);
print_distance(cur_info->length, false, true, 0);
- print_speed(&cur_info->length, &cur_info->time);
+ print_speed(cur_info->length, cur_info->time);
if (track->rte_urls.HasUrlLink()) {
print_string("%s", track->rte_urls.GetUrlLink().url_);
} else {
print_string("%s", "");
}
- *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n\r\n", headers[trkpt_header]);
+ *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n\r\n").arg(headers[trkpt_header]);
}
static void
print_position(wpt);
print_date_and_time(wpt->GetCreationTime().toTime_t(), false);
- if IS_VALID_ALT(wpt->altitude) {
+ if (is_valid_alt(wpt->altitude)) {
print_distance(wpt->altitude, true, false, 0);
}
dist = waypt_distance_ex(prev, wpt);
print_distance(dist, false, true, 0);
print_date_and_time(delta, true);
- print_speed(&dist, &delta);
+ print_speed(dist, delta);
print_course(prev, wpt);
}
*fout << "\r\n";
static void
garmin_txt_wr_init(const QString& fname)
{
- memset(>xt_flags, 0, sizeof(gtxt_flags));
+ gtxt_flags = {};
fout = new gpsbabel::TextStream;
fout->open(fname, QIODevice::WriteOnly, MYNAME, "windows-1252");
if (waypoints > 0) {
wpt_a_ct = 0;
- wpt_a = new const Waypoint*[waypoints]{};
+ wpt_a = new const Waypoint*[waypoints] {};
waypt_disp_all(enum_waypt_cb);
route_disp_all(nullptr, nullptr, enum_waypt_cb);
auto sort_waypt_lambda = [](const Waypoint* wa, const Waypoint* wb)->bool {
};
std::sort(wpt_a, wpt_a + waypoints, sort_waypt_lambda);
- *fout << QString::asprintf("Header\t%s\r\n\r\n", headers[waypt_header]);
+ *fout << QStringLiteral("Header\t%1\r\n\r\n").arg(headers[waypt_header]);
for (int i = 0; i < waypoints; i++) {
write_waypt(wpt_a[i]);
}
/* helpers */
static void
-free_header(const header_type ht)
+free_headers()
{
- for (int i = 0; i < MAX_HEADER_FIELDS; i++) {
- header_lines[ht][i].clear();
- }
- header_ct[ht] = 0;
- memset(header_fields[ht], 0, sizeof(header_fields[ht]));
+ std::for_each(header_mapping_info.begin(), header_mapping_info.end(),
+ [](auto& list)->void { list.clear(); });
}
// Super simple attempt to convert strftime/strptime spec to Qt spec.
case '%':
if (i < l-1) {
switch (s[++i]) {
- case 'd': q += "dd"; continue;
- case 'm': q += "MM"; continue ;
- case 'y': q += "yy"; continue ;
- case 'Y': q += "yyyy"; continue ;
- case 'H': q += "hh"; continue ;
- case 'M': q += "mm"; continue ;
- case 'S': q += "ss"; continue ;
- case 'A': q += "dddd"; continue ;
- case 'a': q += "ddd"; continue ;
- case 'B': q += "MMMM"; continue ;
- case 'C': q += "yy"; continue ;
- case 'D': q += "MM/dd/yyyy"; continue ;
- case 'T': q += "hh:mm:ss"; continue ;
- case 'F': q += "yyyy-MM-dd"; continue ;
- default: q += s[i+1]; break ;
+ case 'd':
+ q += "dd";
+ continue;
+ case 'm':
+ q += "MM";
+ continue;
+ case 'y':
+ q += "yy";
+ continue;
+ case 'Y':
+ q += "yyyy";
+ continue;
+ case 'H':
+ q += "hh";
+ continue;
+ case 'M':
+ q += "mm";
+ continue;
+ case 'S':
+ q += "ss";
+ continue;
+ case 'A':
+ q += "dddd";
+ continue;
+ case 'a':
+ q += "ddd";
+ continue;
+ case 'B':
+ q += "MMMM";
+ continue;
+ case 'C':
+ q += "yy";
+ continue;
+ case 'D':
+ q += "MM/dd/yyyy";
+ continue;
+ case 'T':
+ q += "hh:mm:ss";
+ continue;
+ case 'F':
+ q += "yyyy-MM-dd";
+ continue;
+ default:
+ q += s[i+1];
+ break;
}
}
break;
/* data parsers */
-// This could return an optional QDateTime instead or a pair.
-static bool
-parse_date_and_time(const QString& str, QDateTime* value)
+static QDateTime
+parse_date_and_time(const QString& str)
{
QString timespec = strftime_to_timespec(date_time_format);
- QDateTime dt;
- dt = QDateTime::fromString(QString(str).trimmed(), timespec);
- bool dt_is_valid = dt.isValid();
- if (dt_is_valid) {
- *value = dt;
- }
- return dt_is_valid;
+ return QDateTime::fromString(QString(str).trimmed(), timespec);
}
static uint16_t
static void
parse_header(const QStringList& lineparts)
{
- int column = -1;
-
- free_header(unknown_header);
-
- for (const auto& str : lineparts) {
- column++;
- header_lines[unknown_header][column] = str;
- header_lines[unknown_header][column] = header_lines[unknown_header][column].toUpper();
- header_ct[unknown_header]++;
- if (header_ct[unknown_header] >= MAX_HEADER_FIELDS) {
- break;
- }
+ header_column_names.clear();
+ for (const auto& name : lineparts) {
+ header_column_names.append(name.toUpper());
}
}
fatal(MYNAME ": Incomplete or invalid file header!");
}
- if (header_ct[unknown_header] <= 0) {
+ if (header_column_names.isEmpty()) {
return;
}
- free_header(ht);
+ header_mapping_info[ht].clear();
- /* make a copy of headers[ht], uppercase, replace "\t" with "\0" */
+ /* make a copy of headers[ht], uppercase, split on "\t" */
+ const QStringList altheader = headers.at(ht).toUpper().split('\t');
- int i = strlen(headers[ht]);
- char* fields = (char*) xmalloc(i + 2);
- strcpy(fields, headers[ht]);
- strcat(fields, "\t");
- char* c = strupper(fields);
- while ((c = strchr(c, '\t'))) {
- *c++ = '\0';
- }
+ int i = -1;
+ for (const auto& name : qAsConst(header_column_names)) {
+ i++;
- for (i = 0; i < header_ct[unknown_header]; i++) {
- auto name = header_lines[ht][i] = header_lines[unknown_header][i];
- header_lines[unknown_header][i].clear();
-
- c = fields;
- int field_no = 1;
- while (*c) {
- if (name.compare(c) == 0) {
- header_fields[ht][i] = field_no;
-#if 0
- printf("Binding field \"%s\" to internal number %d (%d,%d)\n", name, field_no, ht, i);
-#endif
- break;
+ int field_idx = altheader.indexOf(name);
+ if (field_idx >= 0) {
+ int field_no = field_idx + 1;
+ header_mapping_info[ht].append(std::make_pair(name, field_no));
+ if (global_opts.debug_level >= 2) {
+ fprintf(stderr, MYNAME ": Binding field \"%s\" to internal number %d (%d,%d)\n", qPrintable(name), field_no, ht, i);
}
- field_no++;
- c = c + strlen(c) + 1;
+ } else {
+ warning(MYNAME ": Field %s not recognized!\n", qPrintable(name));
}
}
- header_ct[unknown_header] = 0;
- xfree(fields);
+ header_column_names.clear();
}
static void
wpt->fs.FsChainAdd(gmsd);
for (const auto& str : lineparts) {
- column++;
+ if (++column >= header_mapping_info[waypt_header].size()) {
+ warning(MYNAME ": too many fields in Waypoint record!\n");
+ break;
+ }
int i;
double d;
- int field_no = header_fields[waypt_header][column];
+ const auto& [name, field_no] = header_mapping_info[waypt_header].at(column);
switch (field_no) {
case 1:
garmin_fs_t::set_country(gmsd, str);
garmin_fs_t::set_cc(gmsd, gt_get_icao_cc(str, wpt->shortname));
break;
- case 16: {
- QDateTime ct;
- if (parse_date_and_time(str, &ct)) {
- wpt->SetCreationTime(ct);
+ case 16:
+ if (QDateTime dt = parse_date_and_time(str); dt.isValid()) {
+ wpt->SetCreationTime(dt);
}
- }
break;
case 17: {
wpt->AddUrlLink(str);
bind_fields(route_header);
for (const auto& str : lineparts) {
- column++;
- int field_no = header_fields[route_header][column];
+ if (++column >= header_mapping_info[route_header].size()) {
+ warning(MYNAME ": too many fields in Route record!\n");
+ break;
+ }
+ const auto& [name, field_no] = header_mapping_info[route_header].at(column);
switch (field_no) {
case 1:
if (!str.isEmpty()) {
bind_fields(track_header);
auto* trk = new route_head;
for (const auto& str : lineparts) {
- column++;
- int field_no = header_fields[track_header][column];
+ if (++column >= header_mapping_info[track_header].size()) {
+ warning(MYNAME ": too many fields in Track record!\n");
+ break;
+ }
+ const auto& [name, field_no] = header_mapping_info[track_header].at(column);
switch (field_no) {
case 1:
if (!str.isEmpty()) {
bind_fields(rtept_header);
for (const auto& str : lineparts) {
- column++;
- int field_no = header_fields[rtept_header][column];
+ if (++column >= header_mapping_info[rtept_header].size()) {
+ warning(MYNAME ": too many fields in Route Waypoint record!\n");
+ break;
+ }
+ const auto& [name, field_no] = header_mapping_info[rtept_header].at(column);
switch (field_no) {
case 1:
if (str.isEmpty()) {
auto* wpt = new Waypoint;
for (const auto& str : lineparts) {
- column++;
+ if (++column >= header_mapping_info[trkpt_header].size()) {
+ warning(MYNAME ": too many fields in Trackpoint record!\n");
+ break;
+ }
double x;
if (str.isEmpty()) {
continue;
}
- int field_no = header_fields[trkpt_header][column];
+ const auto& [name, field_no] = header_mapping_info[trkpt_header].at(column);
+
switch (field_no) {
case 1:
parse_coordinates(str, datum_index, grid_index,
&wpt->latitude, &wpt->longitude, MYNAME);
break;
- case 2: {
- QDateTime ct;
- if (parse_date_and_time(str, &ct)) {
- wpt->SetCreationTime(ct);
+ case 2:
+ if (QDateTime dt = parse_date_and_time(str); dt.isValid()) {
+ wpt->SetCreationTime(dt);
}
- }
break;
case 3:
if (parse_distance(str, &x, 1, MYNAME)) {
static void
garmin_txt_rd_init(const QString& fname)
{
- memset(>xt_flags, 0, sizeof(gtxt_flags));
+ gtxt_flags = {};
fin = new gpsbabel::TextStream;
fin->open(fname, QIODevice::ReadOnly, MYNAME, "windows-1252");
- memset(&header_ct, 0, sizeof(header_ct));
+
+ free_headers();
+ header_column_names.clear();
datum_index = -1;
grid_index = (grid_type) -1;
static void
garmin_txt_rd_deinit()
{
- for (header_type h = waypt_header; h <= unknown_header; ++h) {
- free_header(h);
- }
+ free_headers();
+ header_column_names.clear();
fin->close();
delete fin;
fin = nullptr;
--- /dev/null
+Grid Lat/Lon hddd°mm.mmm'\r
+Datum WGS 84\r
+\r
+Header Description Name Type Position Altitude Depth Proximity Temperature Display Mode Color Symbol Facility City State Country Date Modified Link Categories\r
+\r
+Waypoint 001 Map Line N50 29.556188732 E12 06.325848140 Symbol Unknown Waypoint 28/03/2006 00:10:37\r
+Waypoint 002 Map Intersection N50 29.556188732 E12 06.325848140 Symbol Unknown Waypoint 28/03/2006 00:10:37\r
+Waypoint 003 Map Intersection N50 29.656610638 E12 06.307823695 Symbol Unknown Waypoint 28/03/2006 00:10:37\r
+Waypoint 004 Map Line N50 29.630036652 E12 06.366030984 Symbol Unknown Waypoint 28/03/2006 00:10:37\r
+Waypoint 005 Map Line N50 29.630036652 E12 06.366030984 Symbol Unknown Waypoint 28/03/2006 00:10:37\r
+Waypoint 006 Map Intersection N50 29.602537304 E12 06.426270045 Symbol Unknown Waypoint 28/03/2006 00:10:37\r
+Waypoint 007 Map Line N50 29.619586095 E12 06.429106481 Symbol Unknown Waypoint 28/03/2006 00:10:37\r
+Waypoint Dummy airport (Germany) ED_X Airport N51 53.627961650 E12 58.676564991 Symbol & Name Unknown Airport FAC1 CITY1 Germany (civil) 28/03/2006 01:38:07\r
+Waypoint Dummy airport (Spain) GC_X Airport N38 37.919719778 W3 10.443304181 Symbol & Name Unknown Airport FAC2 CITY2 Spain (Canary Islands) 28/03/2006 01:42:01\r
+Waypoint Jahnstrasse 11 Jahnstrasse User Waypoint N50 29.619998485 E12 06.429000869 Symbol & Description Unknown Flag, Red 31/03/2006 21:48:22\r
+Waypoint Dummy airport (France) LF_X Airport N46 23.256332763 E3 29.896638617 Symbol & Name Unknown Airport FAC3 CITY3 France (Metropolitan France; including Saint-Pierre and Miquelon) 28/03/2006 01:40:32\r
+Waypoint Dummy airport (Italy) LI_X Airport N43 18.873018846 E12 09.693240859 Symbol & Name Unknown Heliport FAC4 CITY4 Italy 28/03/2006 01:43:25\r
+Waypoint Liebknechtstrasse 90 Liebknechtstrasse User Waypoint N50 29.630041681 E12 06.366015896 Symbol & Name Unknown Waypoint 31/03/2006 21:49:30\r
+Waypoint Start NARVA User Waypoint N50 29.556958191 E12 06.326884143 391 m Symbol Unknown Flag, Green 31/03/2006 21:49:26 http://www.narva-light.de Category 15\r
+\r
+\r
+Header Length Name Course Waypoints Link\r
+\r
+Route 4087 km ED_X-LF_X 232° true 4 waypoints\r
+\r
+Header Distance Waypoint Name Leg Length Course\r
+\r
+Route Waypoint 0 m ED_X\r
+Route Waypoint 1936 km GC_X 1936 km 227° true\r
+Route Waypoint 3323 km LI_X 1388 km 63° true\r
+Route Waypoint 4087 km LF_X 764 km 300° true\r
+\r
+Route 394 m NARVA to Jahnstrasse 46° true 10 waypoints\r
+\r
+Header Distance Waypoint Name Leg Length Course\r
+\r
+Route Waypoint 0 m NARVA\r
+Route Waypoint 2 m 001 2 m 221° true\r
+Route Waypoint 2 m 002 0 m 0° true\r
+Route Waypoint 189 m 003 188 m 353° true\r
+Route Waypoint 274 m 004 85 m 126° true\r
+Route Waypoint 274 m Liebknechtstrasse 0 m 298° true\r
+Route Waypoint 274 m 005 0 m 118° true\r
+Route Waypoint 361 m 006 88 m 126° true\r
+Route Waypoint 393 m 007 32 m 6° true\r
+Route Waypoint 394 m Jahnstrasse 1 m 351° true\r
+\r
+\r
+Header Start Time Name Elapsed Time Length Average Speed Link\r
+\r
+Track 01/05/2005 15:02:47 ACTIVE LOG 006 0:33:09 653 m 1.2 kph\r
+\r
+Header Time Position Altitude Depth Temperature Leg Length Leg Time Leg Speed Leg Course\r
+\r
+Trackpoint 01/05/2005 15:02:47 N51 18.776294924 E12 24.789910130 161 m 0.0 m\r
+Trackpoint 01/05/2005 15:03:25 N51 18.772960603 E12 24.794909097 154 m 0.0 m 8 m 0:00:38 0.8 kph 137° true\r
+Trackpoint 01/05/2005 15:03:39 N51 18.771295957 E12 24.794909097 148 m 0.0 m 3 m 0:00:14 0.8 kph 180° true\r
+Trackpoint 01/05/2005 15:04:16 N51 18.769626282 E12 24.798243418 139 m 0.0 m 5 m 0:00:37 0.5 kph 129° true\r
+Trackpoint 01/05/2005 15:05:02 N51 18.769626282 E12 24.796573743 145 m 0.0 m 2 m 0:00:46 0.2 kph 270° true\r
+Trackpoint 01/05/2005 15:05:45 N51 18.769626282 E12 24.798243418 134 m 0.0 m 2 m 0:00:43 0.2 kph 90° true\r
+Trackpoint 01/05/2005 15:06:44 N51 18.766296990 E12 24.799908064 131 m 0.0 m 6 m 0:00:59 0.4 kph 163° true\r
+Trackpoint 01/05/2005 15:07:50 N51 18.766296990 E12 24.799908064 130 m 0.0 m 0 m 0:01:06 0 kph 0° true\r
+Trackpoint 01/05/2005 15:08:19 N51 18.764627315 E12 24.799908064 132 m 0.0 m 3 m 0:00:29 0.4 kph 180° true\r
+Trackpoint 01/05/2005 15:11:16 N51 18.767961636 E12 24.798243418 144 m 0.0 m 6 m 0:02:57 0.1 kph 343° true\r
+Trackpoint 01/05/2005 15:12:34 N51 18.774630278 E12 24.806576706 147 m 0.0 m 16 m 0:01:18 0.7 kph 38° true\r
+Trackpoint 01/05/2005 15:13:18 N51 18.779629245 E12 24.828242250 145 m 0.0 m 27 m 0:00:44 2 kph 70° true\r
+Trackpoint 01/05/2005 15:13:27 N51 18.782963566 E12 24.828242250 145 m 0.0 m 6 m 0:00:09 2 kph 0° true\r
+Trackpoint 01/05/2005 15:13:37 N51 18.782963566 E12 24.829906896 135 m 0.0 m 2 m 0:00:10 0.7 kph 90° true\r
+Trackpoint 01/05/2005 15:13:46 N51 18.786292858 E12 24.829906896 135 m 0.0 m 6 m 0:00:09 2 kph 0° true\r
+Trackpoint 01/05/2005 15:14:03 N51 18.792961501 E12 24.833241217 136 m 0.0 m 13 m 0:00:17 3 kph 17° true\r
+Trackpoint 01/05/2005 15:14:16 N51 18.797960468 E12 24.838240184 135 m 0.0 m 11 m 0:00:13 3 kph 32° true\r
+Trackpoint 01/05/2005 15:14:26 N51 18.796295822 E12 24.843239151 139 m 0.0 m 7 m 0:00:10 2 kph 118° true\r
+Trackpoint 01/05/2005 15:14:30 N51 18.796295822 E12 24.846573472 139 m 0.0 m 4 m 0:00:04 3 kph 90° true\r
+Trackpoint 01/05/2005 15:15:06 N51 18.782963566 E12 24.876572303 141 m 0.0 m 43 m 0:00:36 4 kph 125° true\r
+Trackpoint 01/05/2005 15:15:27 N51 18.777959570 E12 24.889909588 140 m 0.0 m 18 m 0:00:21 3 kph 121° true\r
+Trackpoint 01/05/2005 15:15:39 N51 18.774630278 E12 24.896573201 140 m 0.0 m 10 m 0:00:12 3 kph 129° true\r
+Trackpoint 01/05/2005 15:25:31 N51 18.776294924 E12 24.898242876 152 m 0.0 m 4 m 0:09:52 0.0 kph 32° true\r
+Trackpoint 01/05/2005 15:25:40 N51 18.776294924 E12 24.898242876 152 m 0.0 m 0 m 0:00:09 0 kph 0° true\r
+Trackpoint 01/05/2005 15:29:18 N51 18.777959570 E12 24.896573201 155 m 0.0 m 4 m 0:03:38 0.1 kph 328° true\r
+Trackpoint 01/05/2005 15:30:30 N51 18.789627180 E12 24.874907658 149 m 0.0 m 33 m 0:01:12 2 kph 311° true\r
+Trackpoint 01/05/2005 15:30:37 N51 18.789627180 E12 24.873243012 150 m 0.0 m 2 m 0:00:07 1.0 kph 270° true\r
+Trackpoint 01/05/2005 15:30:47 N51 18.789627180 E12 24.866574369 151 m 0.0 m 8 m 0:00:10 3 kph 270° true\r
+Trackpoint 01/05/2005 15:30:48 N51 18.789627180 E12 24.863240048 151 m 0.0 m 4 m 0:00:01 14 kph 270° true\r
+Trackpoint 01/05/2005 15:30:52 N51 18.799630143 E12 24.834905863 150 m 0.0 m 38 m 0:00:04 34 kph 299° true\r
+Trackpoint 01/05/2005 15:30:57 N51 18.821295686 E12 24.799908064 150 m 0.0 m 57 m 0:00:05 41 kph 315° true\r
+Trackpoint 01/05/2005 15:31:03 N51 18.839626908 E12 24.771573879 150 m 0.0 m 47 m 0:00:06 28 kph 316° true\r
+Trackpoint 01/05/2005 15:31:10 N51 18.852959163 E12 24.749908336 150 m 0.0 m 35 m 0:00:07 18 kph 315° true\r
+Trackpoint 01/05/2005 15:32:38 N51 18.877959028 E12 24.573239610 143 m 0.0 m 210 m 0:01:28 9 kph 283° true\r
+Trackpoint 01/05/2005 15:32:45 N51 18.877959028 E12 24.569910318 141 m 0.0 m 4 m 0:00:07 2 kph 270° true\r
+Trackpoint 01/05/2005 15:33:17 N51 18.877959028 E12 24.569910318 143 m 0.0 m 0 m 0:00:32 0 kph 0° true\r
+Trackpoint 01/05/2005 15:33:42 N51 18.877959028 E12 24.566575997 139 m 0.0 m 4 m 0:00:25 0.6 kph 270° true\r
+Trackpoint 01/05/2005 15:33:54 N51 18.877959028 E12 24.561572000 139 m 0.0 m 6 m 0:00:12 2 kph 270° true\r
+Trackpoint 01/05/2005 15:34:04 N51 18.877959028 E12 24.561572000 138 m 0.0 m 0 m 0:00:10 0 kph 0° true\r
+Trackpoint 01/05/2005 15:34:20 N51 18.877959028 E12 24.561572000 139 m 0.0 m 0 m 0:00:16 0 kph 0° true\r
+Trackpoint 01/05/2005 15:35:45 N51 18.877959028 E12 24.561572000 144 m 0.0 m 0 m 0:01:25 0 kph 0° true\r
+Trackpoint 01/05/2005 15:35:56 N51 18.877959028 E12 24.561572000 145 m 0.0 m 0 m 0:00:11 0 kph 0° true\r